#pragma once

#include <HpsFramework/Locker.hpp>
#include <HpsFramework/NetOperations.hpp>
#include <HpsFramework/ScopedLocker.hpp>
#include <HpsFramework/Protocol.hpp>
#include <HpsFramework/ProtocolMessage.hpp>
#include <HpsFramework/Socket.hpp>

namespace HpsFramework
{

	namespace Network
	{

		class Client
			: public Network::Socket
		{

			/// For access to is[?]Set() functions
			friend class Server;

			friend class ServerWriter;

		public:
			/// Generic ID type for clients
			typedef int IdType;

			/// Is this client waiting for removal?
			bool _remove;

			/// Is this client disconnected?
			bool _disconnect;

			/// Network packet writer
			NetWriter _writer;

			/// Network packet reader
			NetReader _reader;

			/// Server instance
			class Server* _server;

		protected:
			/// Current client Id
			IdType _uid;

			/**
			 * Is close action set? (required for cleanup)
			 **/
			bool isDisconnectionSet();

			/**
			 * Flag for knowing if this session must be killed
			 **/
			bool isRemoveSet();

			/**
			 * Sets the disconnection flag
			 **/
			void setDisconnection();

			/**
			 * Sets the removal flag
			 **/
			void setRemoval();

		public:
			/**
			 * Client constructor
			 **/
			Client(Socket& other, class Server* srv, IdType id);

			/**
			 * Client destructor
			 **/
			virtual ~Client(void);

			/**
			 * Gets the current outgoing buffer manager ptr
			 **/
			NetWriter* getWriter();

			/**
			 * Gets the current incoming buffer manager ptr
			 **/
			NetReader* getReader();

			/**
			 * Enqueues RAW buffer for sending
			 **/
			virtual void write ( byteptr buf, size bufSize );

			/**
			 * Enqueues a message
			 **/
			template<class T>
			void writeMessage ( Protocol::ProtocolKey key, Protocol::Message<T>& message )
			{
				std::tr1::shared_ptr<ProtocolBuffer>& buffer = message.createOutputBuffer(key);
				this->writeBuffer(buffer.get());
			}

			/**
			 * Enqueues a ProtocolBuffer for sending
			 **/
			void writeBuffer ( Protocol::ProtocolBuffer* buffer );

			/**
			 * Sets a removal flag, then disconnects and releases
			 * the client resource into the next server update
			 **/
			void disconnect ( bool graceful = true );

			/**
			 * Use disconnect instead! 
			 **/
			virtual void close ( bool graceful = true );

			/**
			 * Gets the current associated server
			 **/
			class Server* getServer();

			/**
			 * Gets the current client id
			 **/
			Client::IdType getId();

		public:
			/**
			 * Event raised when client is going to 
			 * be disconnected
			 **/
			virtual void onDisconnect() = 0;

			/**
			 * Event raised when new incoming data
			 * appears
			 **/
			virtual size onReceive ( byteptr buffer, size bufSize ) = 0;

			/**
			 * Event raised when connection is successfully 
			 * created and the first WSARecv is called
			 **/
			virtual void onConnect() = 0;

		};

	}

}

